﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Fysh.ComputerScience.Genetics
{
    public class Swarm <T>
    {
        private Particle<T>[] _population;
        private Particle<T> _fitestParticle;
        private ParticleFitnessDelegate _evalutateFitness;
        private ParticleReproduceDelegate _reproduce;
    
        public Swarm(ParticleFitnessDelegate particleFitnessDelegate, ParticleReproduceDelegate particleReproduceDelegate)
        {
            _evalutateFitness = particleFitnessDelegate;
            _reproduce = particleReproduceDelegate;
        }

        /// <summary>
        /// Iterates through a number of generations of the population
        /// </summary>
        public void Iterate(int generations)
        {
            double maxFitness;
            
            for (int count = 0; count < generations; count++)
                for (int i = 0; i < _population.Length; i++)
                {
                    //evaluate the fitness of the current particle
                    _population[i].CurrentSolutionFitness = _evalutateFitness(_population[i].CurrentSolution);

                    //update the global fitness
                    if (_population[i].CurrentSolutionFitness > _fitestParticle.CurrentSolutionFitness)
                        _fitestParticle = _population[i];
                    
                    //update fitest particle property
                    if(_population[i].CurrentSolutionFitness > _population[i].BestSolutionFitness)
                        _population[i].BestSolution = _population[i].CurrentSolution;

                    //identify the fitest neighbour of the current particle
                    maxFitness = double.MinValue;
                    for(int j = 0; j < _population[i].Neighbourhood.Length; j++)
                        if(_population[i].Neighbourhood[j].CurrentSolutionFitness > maxFitness)
                        {
                            maxFitness = _population[i].Neighbourhood[j].CurrentSolutionFitness;
                            _population[i].FitestNeighbour = _population[i].Neighbourhood[j];
                        }

                    //reproduce the current particle with the fitest
                    _population[i].CurrentSolution = _reproduce(_population[i].CurrentSolution, _population[i].FitestNeighbour.CurrentSolution);
                }
        }

        /// <summary>
        /// Steps through a single iteration of the population
        /// </summary>
        public void Iterate()
        {
            Iterate(1); 
        }
    
        public delegate double ParticleFitnessDelegate(T particle);
        public delegate T ParticleReproduceDelegate(T parentA, T parentB);
        
        public T[] Particles
        {
            get
            {
                throw new System.NotImplementedException();
                T[] returnVar = new T[_population.Length];
                for (int i = 0; i < _population.Length; i++)
                    returnVar[i] = _population[i].CurrentSolution;
                return returnVar;
            }
            set
            {
                _population = new Particle<T>[value.Length];
                for (int i = 0; i < value.Length; i++)
                    _population[i].CurrentSolution = value[i];
            }
        }

        public T BestSolution
        {
            get
            {
                return _fitestParticle.CurrentSolution;
            }
        }
    }
}
